跳到主要内容

如何实现单实例应用?second-instance 事件怎么用?

考察点​

  • 单实例锁 API 的使用
  • 二次启动时的窗口聚焦与参数透传(agent 场景高频需求)

参考答案​

基本实现​

const { app } = require('electron');

// 请求单实例锁,必须在 ready 之前调用
const gotTheLock = app.requestSingleInstanceLock();

if (!gotTheLock) {
// 没拿到锁说明已有实例在运行,直接退出
app.quit();
} else {
// 已有实例时,第二个实例启动会触发该事件
app.on('second-instance', (event, argv, workingDirectory) => {
const mainWindow = getMainWindow();
if (mainWindow) {
// 窗口最小化时先恢复
if (mainWindow.isMinimized()) mainWindow.restore();
mainWindow.show();
mainWindow.focus();
}
// 解析第二个实例携带的命令行参数(如 deeplink 唤起)
handleCommandLine(argv);
});

app.whenReady().then(createWindow);
}

Agent 场景的典型用法​

1. 二次启动聚焦已有窗口 用户双击桌面图标时,agent 已在后台运行 → 聚焦主窗口而不是再开一个。

2. 解析命令行参数 / DeepLink 唤起

  • Windows 上 deeplink(如 myapp://do-task?id=1)唤起已运行的实例时,URL 会作为 argv 的最后一个参数传进来:
function handleCommandLine(argv) {
// 约定:deeplink 在参数末尾
const deeplink = argv.find((arg) => arg.startsWith('myapp://'));
if (deeplink) {
handleDeeplink(deeplink); // 分发业务路由
}
}
  • macOS 不走 argv,而是 app.on('open-url') 事件(需先用 app.setAsDefaultProtocolClient 注册协议)。

注意点​

  1. requestSingleInstanceLock 必须在 ready 之前调用,否则时机太晚。
  2. second-instance 的回调里窗口可能已销毁,要先判空。
  3. Windows 安装包(Squirrel 时代)的更新快捷方式可能带特殊参数(--squirrel-firstrun 等),注意过滤。
  4. 打包后的 argv 与开发环境不同,取参数时跳过前几项(开发模式前两项是 electron 和入口路径)。

常见追问​

  • 想做多实例怎么办? 不请求锁即可,或者用 process.env 传 profile 实现多开隔离(多开时 userData 目录要分开,否则会报 profile 锁错误)。
  • 锁的粒度? 默认按 userData 目录(即同一应用不同用户数据目录不算同一实例)。